classdef router < handle
    properties
        %router parameters
        coor_x
        coor_y
        queue_size

        %queues
        north_queue
        south_queue
        west_queue
        east_queue
        inject_queue

        %queues occupancy
        north_busy_rate
        south_busy_rate
        west_busy_rate
        east_busy_rate

        %packet routed counters
        north_west_routed_cnt
        north_east_routed_cnt
        south_west_routed_cnt
        south_east_routed_cnt
        west_north_routed_cnt
        west_south_routed_cnt
        east_north_routed_cnt
        east_south_routed_cnt
        north_routed_cnt
        south_routed_cnt
        west_routed_cnt
        east_routed_cnt

        %neighbour routers
        north_router
        south_router
        west_router
        east_router

        %average coordinates
        north_west_avg
        north_east_avg
        south_west_avg
        south_east_avg
        west_north_avg
        west_south_avg
        east_north_avg
        east_south_avg

        %forwarded flits
        north_west_fwd_flits
        north_east_fwd_flits
        south_west_fwd_flits
        south_east_fwd_flits
        west_north_fwd_flits
        west_south_fwd_flits
        east_north_fwd_flits
        east_south_fwd_flits
        north_fwd_flits
        south_fwd_flits
        west_fwd_flits
        east_fwd_flits

        rec_flits

        %direction constansts
        north
        south
        west
        east
        north_west
        north_east
        south_west
        south_east
        received

        pause_value
    end
    methods

        %init router with default values
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function obj = router(coor_x, coor_y)
            obj.coor_x = coor_x;
            obj.coor_y = coor_y;

            obj.queue_size = 10;

            obj.north_queue     = zeros(obj.queue_size,2);
            obj.south_queue     = zeros(obj.queue_size,2);
            obj.west_queue      = zeros(obj.queue_size,2);
            obj.east_queue      = zeros(obj.queue_size,2);
            obj.east_queue      = zeros(obj.queue_size,2);
            obj.inject_queue    = zeros(obj.queue_size,2);

            obj.north_busy_rate = 0;
            obj.south_busy_rate = 0;
            obj.west_busy_rate  = 0;
            obj.east_busy_rate  = 0;

            obj.north_west_routed_cnt = 0;
            obj.north_east_routed_cnt = 0;
            obj.south_west_routed_cnt = 0;
            obj.south_east_routed_cnt = 0;
            obj.west_north_routed_cnt = 0;
            obj.west_south_routed_cnt = 0;
            obj.east_north_routed_cnt = 0;
            obj.east_south_routed_cnt = 0;
            obj.north_routed_cnt      = 0;
            obj.south_routed_cnt      = 0;
            obj.west_routed_cnt       = 0;
            obj.east_routed_cnt       = 0;

            obj.north_west_fwd_flits  = zeros(1,2);
            obj.north_east_fwd_flits  = zeros(1,2);
            obj.south_west_fwd_flits  = zeros(1,2);
            obj.south_east_fwd_flits  = zeros(1,2);
            obj.west_north_fwd_flits  = zeros(1,2);
            obj.west_south_fwd_flits  = zeros(1,2);
            obj.east_north_fwd_flits  = zeros(1,2);
            obj.east_south_fwd_flits  = zeros(1,2);
            obj.north_fwd_flits       = zeros(1,2);
            obj.south_fwd_flits       = zeros(1,2);
            obj.west_fwd_flits        = zeros(1,2);
            obj.east_fwd_flits        = zeros(1,2);
            obj.rec_flits             = zeros(1,2);


            obj.north_west_avg = zeros(1,2);
            obj.north_east_avg = zeros(1,2);
            obj.south_west_avg = zeros(1,2);
            obj.south_east_avg = zeros(1,2);
            obj.west_north_avg = zeros(1,2);
            obj.west_south_avg = zeros(1,2);
            obj.east_north_avg = zeros(1,2);
            obj.east_south_avg = zeros(1,2);

            obj.north      = 1;
            obj.south      = 2;
            obj.west       = 3;
            obj.east       = 4;
            obj.north_west = 5;
            obj.north_east = 6;
            obj.south_west = 7;
            obj.south_east = 8;
            obj.received   = 9;
            obj.pause_value= 0;
        end

        %push flit on the router's queue
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function push_queue(obj, queue_type, value)
            switch queue_type
                case 'north'
                    if (obj.north_busy_rate < obj.queue_size)
                        obj.north_queue(obj.north_busy_rate + 1,:) = value;
                        obj.north_busy_rate = obj.north_busy_rate + 1;
                        obj.process_flit('north');
                    end
                case 'south'
                    if (obj.south_busy_rate < obj.queue_size)
                        obj.south_queue(obj.south_busy_rate + 1,:) = value;
                        obj.south_busy_rate = obj.south_busy_rate + 1;
                        obj.process_flit('south');
                    end
                case 'west'
                    if (obj.west_busy_rate < obj.queue_size)
                        obj.west_queue(obj.west_busy_rate + 1,:) = value;
                        obj.west_busy_rate = obj.west_busy_rate + 1;
                        obj.process_flit('west');
                    end
                case 'east'
                    if (obj.east_busy_rate < obj.queue_size)
                        obj.east_queue(obj.east_busy_rate + 1,:) = value;
                        obj.east_busy_rate = obj.east_busy_rate + 1;
                        obj.process_flit('east');
                    end
                case 'inject'
                    obj.inject_queue(1,:) = value;
                    obj.process_flit('inject');
                otherwise
                    error('Invalid field name.')
            end
        end

        %pop flit from the router's queue
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function value = pop_queue(obj, queue_type)
            switch queue_type
                case 'north'
                    if (obj.north_busy_rate > 0)
                        obj.north_busy_rate = obj.north_busy_rate - 1;
                        value = obj.north_queue(1,:);
                        obj.north_queue(1:obj.north_busy_rate,:) = obj.north_queue(2:obj.north_busy_rate + 1,:);
                        obj.north_queue(obj.north_busy_rate + 1,:) = [0,0];
                    else
                        value = [0,0];
                    end
                case 'south'
                    if (obj.south_busy_rate > 0)
                        obj.south_busy_rate = obj.south_busy_rate - 1;
                        value = obj.south_queue(1,:);
                        obj.south_queue(1:obj.south_busy_rate,:) = obj.south_queue(2:obj.south_busy_rate + 1,:);
                        obj.south_queue(obj.south_busy_rate + 1,:) = [0,0];
                    else
                        value = [0,0];
                    end
                case 'west'
                    if (obj.west_busy_rate > 0)
                        obj.west_busy_rate = obj.west_busy_rate - 1;
                        value = obj.west_queue(1,:);
                        obj.west_queue(1:obj.west_busy_rate,:) = obj.west_queue(2:obj.west_busy_rate + 1,:);
                        obj.west_queue(obj.west_busy_rate + 1,:) = [0,0];
                    else
                        value = [0,0];
                    end
                case 'east'
                    if (obj.east_busy_rate > 0)
                        obj.east_busy_rate = obj.east_busy_rate - 1;
                        value = obj.east_queue(1,:);
                        obj.east_queue(1:obj.east_busy_rate,:) = obj.east_queue(2:obj.east_busy_rate + 1,:);
                        obj.east_queue(obj.east_busy_rate + 1,:) = [0,0];
                    else
                        value = [0,0];
                    end
                case 'inject'
                    value = obj.inject_queue(1,:);
                otherwise
                    error('Invalid field name.')
            end
        end

        %draw line in north direction
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function draw_line_north(obj)
            line([obj.coor_x, obj.coor_x],[obj.coor_y, obj.coor_y + 1],'Color','m');
            pause(obj.pause_value);
            line([obj.coor_x, obj.coor_x],[obj.coor_y, obj.coor_y + 1],'Color','k');
        end

        %draw line in south direction
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function draw_line_south(obj)
            line([obj.coor_x, obj.coor_x],[obj.coor_y, obj.coor_y - 1],'Color','m');
            pause(obj.pause_value);
            line([obj.coor_x, obj.coor_x],[obj.coor_y, obj.coor_y - 1],'Color','k');
        end

        %draw line in west direction
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function draw_line_west(obj)
            line([obj.coor_x, obj.coor_x - 1],[obj.coor_y, obj.coor_y],'Color','m');
            pause(obj.pause_value);
            line([obj.coor_x, obj.coor_x - 1],[obj.coor_y, obj.coor_y],'Color','k');
        end

        %draw line in east direction
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function draw_line_east(obj)
            line([obj.coor_x, obj.coor_x + 1],[obj.coor_y, obj.coor_y],'Color','m');
            pause(obj.pause_value);
            line([obj.coor_x, obj.coor_x + 1],[obj.coor_y, obj.coor_y],'Color','k');
        end

        %draw line when flit received
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function draw_line_received(obj)
            line([obj.coor_x, obj.coor_x],[obj.coor_y, obj.coor_y],'Marker','o','MarkerSize',12,'MarkerEdgeColor','k','MarkerFaceColor','b');
        end

        %route flit in north directiom
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function status = route_north(obj, flit)
            if (isobject(obj.north_router))
                if (obj.north_router.north_busy_rate < obj.north_router.queue_size)
                    obj.draw_line_north;
                    if (obj.coor_y <= flit(2))
                        if (obj.coor_x < flit(1))
                            [row_num, col_num] = size(obj.north_east_fwd_flits);
                            obj.north_east_fwd_flits(row_num + 1, :) = flit;
                            obj.north_east_routed_cnt = obj.north_east_routed_cnt + 1;
                            obj.north_east_avg = (sum(obj.north_east_fwd_flits) + sum(obj.north_fwd_flits))/(obj.north_east_routed_cnt + obj.north_routed_cnt);
                      elseif (obj.coor_x > flit(1))
                            [row_num, col_num] = size(obj.north_west_fwd_flits);
                            obj.north_west_fwd_flits(row_num + 1, :) = flit;
                            obj.north_west_routed_cnt = obj.north_west_routed_cnt + 1;
                            obj.north_west_avg = (sum(obj.north_west_fwd_flits) + sum(obj.north_fwd_flits))/(obj.north_west_routed_cnt + obj.north_routed_cnt);
                        elseif (obj.coor_x == flit(1))
                            [row_num, col_num] = size(obj.north_fwd_flits);
                            obj.north_fwd_flits(row_num + 1, :) = flit;
                            obj.north_routed_cnt = obj.north_routed_cnt + 1;
                            obj.north_west_avg = (sum(obj.north_west_fwd_flits) + sum(obj.north_fwd_flits))/(obj.north_west_routed_cnt + obj.north_routed_cnt);
                            obj.north_east_avg = (sum(obj.north_east_fwd_flits) + sum(obj.north_fwd_flits))/(obj.north_east_routed_cnt + obj.north_routed_cnt);
                        end
                    end
                    obj.north_router.push_queue('south', flit);
                    disp(['[',int2str(flit(1)),',',int2str(flit(2)),']:  (',int2str(obj.coor_x),',', int2str(obj.coor_y),') -> (', int2str(obj.coor_x),',', int2str(obj.coor_y + 1),')'])
                    status = 'routing_ok';
                else
                    status = 'routing_err';
                end
            else
                status = 'routing_err';
            end
        end

        %route flit in south directiom
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function status = route_south(obj, flit)
            if (isobject(obj.south_router))
                if (obj.south_router.south_busy_rate < obj.south_router.queue_size)
                    obj.draw_line_south;
                    if (obj.coor_y >= flit(2))
                        if (obj.coor_x < flit(1))
                            [row_num, col_num] = size(obj.south_east_fwd_flits);
                            obj.south_east_fwd_flits(row_num + 1, :) = flit;
                            obj.south_east_routed_cnt = obj.south_east_routed_cnt + 1;
                            obj.south_east_avg = (sum(obj.south_east_fwd_flits) + sum(obj.south_fwd_flits))/(obj.south_east_routed_cnt + obj.south_routed_cnt);
                        elseif (obj.coor_x > flit(1))
                            [row_num, col_num] = size(obj.south_west_fwd_flits);
                            obj.south_west_fwd_flits(row_num + 1, :) = flit;
                            obj.south_west_routed_cnt = obj.south_west_routed_cnt + 1;
                            obj.south_west_avg = (sum(obj.south_west_fwd_flits) + sum(obj.south_fwd_flits))/(obj.south_west_routed_cnt + obj.south_routed_cnt);
                        elseif (obj.coor_x == flit(1))
                            [row_num, col_num] = size(obj.south_fwd_flits);
                            obj.south_fwd_flits(row_num + 1, :) = flit;
                            obj.south_routed_cnt = obj.south_routed_cnt + 1;
                            obj.south_west_avg = (sum(obj.south_west_fwd_flits) + sum(obj.south_fwd_flits))/(obj.south_west_routed_cnt + obj.south_routed_cnt);
                            obj.south_east_avg = (sum(obj.south_east_fwd_flits) + sum(obj.south_fwd_flits))/(obj.south_east_routed_cnt + obj.south_routed_cnt);
                        end
                    end
                    obj.south_router.push_queue('north', flit);
                    disp(['[',int2str(flit(1)),',',int2str(flit(2)),']:  (',int2str(obj.coor_x),',', int2str(obj.coor_y),') -> (', int2str(obj.coor_x),',', int2str(obj.coor_y - 1),')'])
                    status = 'routing_ok';
                else
                    status = 'routing_err';
                end
            else
                status = 'routing_err';
            end
        end

        %route flit in west directiom
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function status = route_west(obj, flit)
            if (isobject(obj.west_router))
                if (obj.west_router.west_busy_rate < obj.west_router.queue_size)
                    obj.draw_line_west;
                    if (obj.coor_x >= flit(1))
                        if (obj.coor_y < flit(2))
                            [row_num, col_num] = size(obj.west_north_fwd_flits);
                            obj.west_north_fwd_flits(row_num + 1, :) = flit;
                            obj.west_north_routed_cnt = obj.west_north_routed_cnt + 1;
                            obj.west_north_avg = (sum(obj.west_north_fwd_flits) + sum(obj.west_fwd_flits))/(obj.west_north_routed_cnt + obj.west_routed_cnt);
                        elseif (obj.coor_y > flit(2))
                            [row_num, col_num] = size(obj.west_south_fwd_flits);
                            obj.west_south_fwd_flits(row_num + 1, :) = flit;
                            obj.west_south_routed_cnt = obj.west_south_routed_cnt + 1;
                            obj.west_south_avg = (sum(obj.west_south_fwd_flits) + sum(obj.west_fwd_flits))/(obj.west_south_routed_cnt + obj.west_routed_cnt);
                        elseif (obj.coor_y == flit(2))
                            [row_num, col_num] = size(obj.west_fwd_flits);
                            obj.west_fwd_flits(row_num + 1, :) = flit;
                            obj.west_routed_cnt = obj.west_routed_cnt + 1;
                            obj.west_south_avg = (sum(obj.west_south_fwd_flits) + sum(obj.west_fwd_flits))/(obj.west_south_routed_cnt + obj.west_routed_cnt);
                            obj.west_north_avg = (sum(obj.west_north_fwd_flits) + sum(obj.west_fwd_flits))/(obj.west_north_routed_cnt + obj.west_routed_cnt);
                        end
                    end
                    obj.west_router.push_queue('east', flit);
                    disp(['[',int2str(flit(1)),',',int2str(flit(2)),']:  (',int2str(obj.coor_x),',', int2str(obj.coor_y),') -> (', int2str(obj.coor_x - 1),',', int2str(obj.coor_y),')'])
                    status = 'routing_ok';
                else
                    status = 'routing_err';
                end
            else
                status = 'routing_err';
            end
        end

        %route flit in east directiom
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function status = route_east(obj, flit)
            if (isobject(obj.east_router))
                if (obj.east_router.east_busy_rate < obj.east_router.queue_size)
                    obj.draw_line_east;
                    if (obj.coor_x <= flit(1))
                        if (obj.coor_y < flit(2))
                            [row_num, col_num] = size(obj.east_north_fwd_flits);
                            obj.east_north_fwd_flits(row_num + 1, :) = flit;
                            obj.east_north_routed_cnt = obj.east_north_routed_cnt + 1;
                            obj.east_north_avg = (sum(obj.east_north_fwd_flits) + sum(obj.east_fwd_flits))/(obj.east_north_routed_cnt + obj.east_routed_cnt);
                        elseif (obj.coor_y > flit(2))
                            [row_num, col_num] = size(obj.east_south_fwd_flits);
                            obj.east_south_fwd_flits(row_num + 1, :) = flit;
                            obj.east_south_routed_cnt = obj.east_south_routed_cnt + 1;
                            obj.east_south_avg = (sum(obj.east_south_fwd_flits) + sum(obj.east_fwd_flits))/(obj.east_south_routed_cnt + obj.east_routed_cnt);
                        elseif (obj.coor_y == flit(2))
                            [row_num, col_num] = size(obj.east_fwd_flits);
                            obj.east_fwd_flits(row_num + 1, :) = flit;
                            obj.east_routed_cnt = obj.east_routed_cnt + 1;
                            obj.east_south_avg = (sum(obj.east_south_fwd_flits) + sum(obj.east_fwd_flits))/(obj.east_south_routed_cnt + obj.east_routed_cnt);
                            obj.east_north_avg = (sum(obj.east_north_fwd_flits) + sum(obj.east_fwd_flits))/(obj.east_north_routed_cnt + obj.east_routed_cnt);
                        end
                    end
                    obj.east_router.push_queue('west', flit);
                    disp(['[',int2str(flit(1)),',',int2str(flit(2)),']:  (',int2str(obj.coor_x),',', int2str(obj.coor_y),') -> (', int2str(obj.coor_x + 1),',', int2str(obj.coor_y),')'])
                    status = 'routing_ok';
                else
                    status = 'routing_err';
                end
            else
                status = 'routing_err';
            end
        end

        %calculate distance beetwen given two routers
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function distance = calculate_distance(obj, node_1, node_2)
%             router_avg_length = sqrt((obj.coor_x - node_1(1))^2 + (obj.coor_y - node_1(2))^2);
%             flit_avg_length = sqrt((node_2(1) - node_1(1))^2 + (node_2(2) - node_1(2))^2);
%             distance = flit_avg_length - router_avg_length;
            distance = abs(node_1(1) - node_2(1)) + abs(node_1(2) - node_2(2));
        end

        %process flits incoming on router's queue
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function direction = calculate_routing_direction(obj, flit)
            flit_coor_x = flit(1);
            flit_coor_y = flit(2);
            if (flit_coor_x > obj.coor_x)
                if (flit_coor_y > obj.coor_y)
                    %route NORTH or EAST
                    direction = obj.north_east;
                elseif (flit_coor_y < obj.coor_y)
                    %route SOUTH or EAST
                    direction = obj.south_east;
                elseif (flit_coor_y == obj.coor_y)
                    %route EAST
                    direction = obj.east;
                end
            elseif (flit_coor_x < obj.coor_x)
                if (flit_coor_y > obj.coor_y)
                    %route NORTH or WEST
                    direction = obj.north_west;
                elseif (flit_coor_y < obj.coor_y)
                    %route SOUTH or WEST
                    direction = obj.south_west;
                elseif (flit_coor_y == obj.coor_y)
                    %route WEST
                    direction = obj.west;
                end
            elseif (flit_coor_x == obj.coor_x)
                if (flit_coor_y > obj.coor_y)
                    %route NORTH
                    direction = obj.north;
                elseif (flit_coor_y < obj.coor_y)
                    %route SOUTH
                    direction = obj.south;
                elseif (flit_coor_y == obj.coor_y)
                    %route NOWHERE - flit addressed to us
                    direction = obj.received;
                end
            end
        end

        %process north or west routing direction
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function direction = process_north_west(obj, flit)
            %statistics on both directions
            if (sum(obj.north_west_avg) > 0 && sum(obj.west_north_avg) > 0)
                if (obj.calculate_distance(obj.north_west_avg, flit) < obj.calculate_distance(obj.west_north_avg, flit))
                    direction = obj.route_north(flit);
                elseif (obj.calculate_distance(obj.north_west_avg, flit) > obj.calculate_distance(obj.west_north_avg, flit))
                    direction = obj.route_west(flit);
                elseif (obj.calculate_distance(obj.north_west_avg, flit) == obj.calculate_distance(obj.west_north_avg, flit))
                    if (abs(obj.coor_y - flit(2)) > abs(obj.coor_x - flit(1)))
                        direction = obj.route_north(flit);
                    elseif (abs(obj.coor_y - flit(2)) < abs(obj.coor_x - flit(1)))
                        direction = obj.route_west(flit);
                    else
                        if (ceil(rand(1)))
                            direction = obj.route_north(flit);
                        else
                            direction = obj.route_west(flit);
                        end
                    end
                end
                %statistics on north direction
            elseif (sum(obj.north_west_avg) > 0 && sum(obj.west_north_avg) == 0)
                direction = obj.route_north(flit);
                %statistics on west direction
            elseif (sum(obj.north_west_avg) == 0 && sum(obj.west_north_avg) > 0)
                direction = obj.route_west(flit);
                %lack of statistics
            elseif (sum(obj.north_west_avg) == 0 && sum(obj.west_north_avg) == 0)
                if (abs(obj.coor_y - flit(2)) > abs(obj.coor_x - flit(1)))
                    direction = obj.route_north(flit);
                elseif (abs(obj.coor_y - flit(2)) < abs(obj.coor_x - flit(1)))
                    direction = obj.route_west(flit);
                else
                    if (ceil(rand(1)))
                        direction = obj.route_north(flit);
                    else
                        direction = obj.route_west(flit);
                    end
                end
            end
        end

        %process north or east routing direction
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function direction = process_north_east(obj, flit)
            %statistics on both directions
            if (sum(obj.north_east_avg) > 0 && sum(obj.east_north_avg) > 0)
                if (obj.calculate_distance(obj.north_east_avg, flit) < obj.calculate_distance(obj.east_north_avg, flit))
                    direction = obj.route_north(flit);
                elseif (obj.calculate_distance(obj.north_east_avg, flit) > obj.calculate_distance(obj.east_north_avg, flit))
                    direction = obj.route_east(flit);
                elseif (obj.calculate_distance(obj.north_east_avg, flit) == obj.calculate_distance(obj.east_north_avg, flit))
                    if (abs(obj.coor_y - flit(2)) > abs(obj.coor_x - flit(1)))
                        direction = obj.route_north(flit);
                    elseif (abs(obj.coor_y - flit(2)) < abs(obj.coor_x - flit(1)))
                        direction = obj.route_east(flit);
                    else
                        if (ceil(rand(1)))
                            direction = obj.route_north(flit);
                        else
                            direction = obj.route_east(flit);
                        end
                    end
                end
                %statistics on north direction
            elseif (sum(obj.north_east_avg) > 0 && sum(obj.east_north_avg) == 0)
                direction = obj.route_north(flit);
                %statistics on east direction
            elseif (sum(obj.north_east_avg) == 0 && sum(obj.east_north_avg) > 0)
                direction = obj.route_east(flit);
                %lack of statistics
            elseif (sum(obj.north_east_avg) == 0 && sum(obj.east_north_avg) == 0)
                if (abs(obj.coor_y - flit(2)) > abs(obj.coor_x - flit(1)))
                    direction = obj.route_north(flit);
                elseif (abs(obj.coor_y - flit(2)) < abs(obj.coor_x - flit(1)))
                    direction = obj.route_east(flit);
                else
                    if (ceil(rand(1)))
                        direction = obj.route_north(flit);
                    else
                        direction = obj.route_east(flit);
                    end
                end
            end
        end

        %process south or west routing direction
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function direction = process_south_west(obj, flit)
            %statistics on both directions
            if (sum(obj.south_west_avg) > 0 && sum(obj.west_south_avg) > 0)
                if (obj.calculate_distance(obj.south_west_avg, flit) < obj.calculate_distance(obj.west_south_avg, flit))
                    direction = obj.route_south(flit);
                elseif (obj.calculate_distance(obj.south_west_avg, flit) > obj.calculate_distance(obj.west_south_avg, flit))
                    direction = obj.route_west(flit);
                elseif (obj.calculate_distance(obj.south_west_avg, flit) == obj.calculate_distance(obj.west_south_avg, flit))
                    if (abs(obj.coor_y - flit(2)) > abs(obj.coor_x - flit(1)))
                        direction = obj.route_south(flit);
                    elseif (abs(obj.coor_y - flit(2)) < abs(obj.coor_x - flit(1)))
                        direction = obj.route_west(flit);
                    else
                        if (ceil(rand(1)))
                            direction = obj.route_south(flit);
                        else
                            direction = obj.route_west(flit);
                        end
                    end
                end
                %statistics on south direction
            elseif (sum(obj.south_west_avg) > 0 && sum(obj.west_south_avg) == 0)
                direction = obj.route_south(flit);
                %statistics on west direction
            elseif (sum(obj.south_west_avg) == 0 && sum(obj.west_south_avg) > 0)
                direction = obj.route_west(flit);
                %lack of statistics
            elseif (sum(obj.south_west_avg) == 0 && sum(obj.west_south_avg) == 0)
                if (abs(obj.coor_y - flit(2)) > abs(obj.coor_x - flit(1)))
                    direction = obj.route_south(flit);
                elseif (abs(obj.coor_y - flit(2)) < abs(obj.coor_x - flit(1)))
                    direction = obj.route_west(flit);
                else
                    if (ceil(rand(1)))
                        direction = obj.route_south(flit);
                    else
                        direction = obj.route_west(flit);
                    end
                end
            end
        end

        %process south or east routing direction
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function direction = process_south_east(obj, flit)
            %statistics on both directions
            %if there are statistics in both direction
            if (sum(obj.south_east_avg) > 0 && sum(obj.east_south_avg) > 0)
                %if distance beetwen south east average node and flit dst
                %is smaller than east south distance
                if (obj.calculate_distance(obj.south_east_avg, flit) < obj.calculate_distance(obj.east_south_avg, flit))
                    direction = obj.route_south(flit);
                elseif (obj.calculate_distance(obj.south_east_avg, flit) > obj.calculate_distance(obj.east_south_avg, flit))
                    direction = obj.route_east(flit);
                elseif (obj.calculate_distance(obj.south_east_avg, flit) == obj.calculate_distance(obj.east_south_avg, flit))
                    if (abs(obj.coor_y - flit(2)) > abs(obj.coor_x - flit(1)))
                        direction = obj.route_south(flit);
                    elseif (abs(obj.coor_y - flit(2)) < abs(obj.coor_x - flit(1)))
                        direction = obj.route_east(flit);
                    else
                        if (ceil(rand(1)))
                            direction = obj.route_south(flit);
                        else
                            direction = obj.route_east(flit);
                        end
                    end
                end
                %statistics on south direction
            elseif (sum(obj.south_east_avg) > 0 && sum(obj.east_south_avg) == 0)
                direction = obj.route_south(flit);
                %statistics on east direction
            elseif (sum(obj.south_east_avg) == 0 && sum(obj.east_south_avg) > 0)
                direction = obj.route_east(flit);
                %lack of statistics
            elseif (sum(obj.south_east_avg) == 0 && sum(obj.east_south_avg) == 0)
                if (abs(obj.coor_y - flit(2)) > abs(obj.coor_x - flit(1)))
                    direction = obj.route_south(flit);
                elseif (abs(obj.coor_y - flit(2)) < abs(obj.coor_x - flit(1)))
                    direction = obj.route_east(flit);
                else
                    if (ceil(rand(1)))
                        direction = obj.route_south(flit);
                    else
                        direction = obj.route_east(flit);
                    end
                end
            end
        end

        %process flits incoming on router's queue
        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        function direction = process_flit(obj, queue_type)
            flit = obj.pop_queue(queue_type);
            if (sum(flit) ~= 0)
                direction = obj.calculate_routing_direction(flit);
                switch direction
                    case obj.north
                        direction = obj.route_north(flit);
                    case obj.south
                        direction = obj.route_south(flit);
                    case obj.west
                        direction = obj.route_west(flit);
                    case obj.east
                        direction = obj.route_east(flit);
                    case obj.north_west
                        direction = obj.process_north_west(flit);
                    case obj.north_east
                        direction = obj.process_north_east(flit);
                    case obj.south_west
                        direction = obj.process_south_west(flit);
                    case obj.south_east
                        direction = obj.process_south_east(flit);
                    case obj.received
                        direction = obj.received;
                        obj.draw_line_received;
                end
            end
        end
    end
end
